package com.jxpanda.plugin.initializr.config

import com.baomidou.mybatisplus.generator.config.po.TableInfo
import com.jxpanda.plugin.initializr.config.JsonInjector.Struct.Companion.JAVA_TYPE_MAP
import com.jxpanda.plugin.initializr.toolkit.StringKit
import java.util.*

private const val JSON_TYPE = "json"

/**
 * 结构体标识符前缀
 * */
private const val STRUCT_PREFIX = "STRUCT:"

private const val LEFT_TYPE_SYMBOL = "(RE)*[{\\[]+"
private const val RIGHT_TYPE_SYMBOL = "[}\\]]+$"
private const val JSON_FIELD_COMMENT = "\\([\\u4e00-\\u9fa5\\w,，.。\\s]+\\)"
private const val JSON_FIELD = "([\\w\\s]+:[\\[\\w\\s\\]{}]+$JSON_FIELD_COMMENT[\\s,]*)"
private const val STRUCT_BLOCK = "$LEFT_TYPE_SYMBOL[\\w\\s:\\u4e00-\\u9fa5,，#()\\[\\]{}]*$RIGHT_TYPE_SYMBOL"

/**
 * 结构体文本正则，【约定】以这个前缀作为描述json结构的开始
 * */
private val STRUCT_TEXT_REGEX = Regex("$STRUCT_PREFIX\\w*$STRUCT_BLOCK")

private val STRUCT_BLOCK_REGEX = Regex(STRUCT_BLOCK)

private val JSON_FIELD_REGEX = Regex(JSON_FIELD)

private val LEFT_TYPE_SYMBOL_REGEX = Regex(LEFT_TYPE_SYMBOL)
private val RIGHT_TYPE_SYMBOL_REGEX = Regex(RIGHT_TYPE_SYMBOL)

private val JSON_FIELD_COMMENT_REGEX = Regex(JSON_FIELD_COMMENT)


private const val CFG_KEY_JSON = "json"
private const val CFG_KEY_HAS_JSON = "hasJson"
private const val CFG_KEY_JSON_TYPE_HANDLER = "jsonTypeHandler"

/**
 * Json注入器，从注释中读取json的描述，注入到生成的文件中
 * */
class JsonInjector : InjectorConfig.Injector() {
    override fun buildInjectMap(tableInfo: TableInfo): Map<String, Any> {
        val jsonMap =
            tableInfo.fields.filter { it.type.lowercase(Locale.getDefault()) == JSON_TYPE }.associateBy({ it.name }, {
                Struct.compile(it.comment)
            })
        return mapOf(
            CFG_KEY_JSON to jsonMap,
            CFG_KEY_HAS_JSON to jsonMap.isNotEmpty(),
            CFG_KEY_JSON_TYPE_HANDLER to model.jsonTypeHandler,
            CFG_KEY_INJECT_PACKAGE to jsonMap
                .flatMap { it.value.importPackages }
                .filter { !tableInfo.importPackages.contains(it) }
                .toSet()
        )
    }

    /**
     * 结构体解构的数据模型
     * */
    data class Struct(
        /**
         * 原始的结构体语句，打印出来方便调试
         * */
        val struct: String,
        /**
         * 结构体类型
         * */
        val structType: StructType = StructType.EMPTY,
        /**
         * 转换后的的类型（带泛型）
         * */
        val javaType: String = JAVA_TYPE_OBJECT,
        /**
         * 转换后的类名，默认是空的
         * */
        val className: String = "",
        /**
         * 字段列表
         * */
        val fields: List<JsonField> = listOf(),
        /**
         * 是否需要生成内部类（基本数据类型不需要生成内部类）
         * */
        val generate: Boolean = false,
        /**
         * 需要导入的包
         * */
        val importPackages: Set<String> = emptySet()
    ) {

        companion object {
            private val STRUCT_TYPE_MAP = StructType.values().associateBy { "${it.leftSymbol}${it.rightSymbol}" }
            private val IMPORT_CHECK_LIST = listOf("[", "{}", "BigDecimal")
            private val IMPORT_MAP = mapOf(
                "[" to "java.util.List",
                "{}" to "java.util.Map",
                "BigDecimal" to "java.math.BigDecimal"
            )
            private const val JAVA_TYPE_OBJECT = "Object"
            const val JAVA_TYPE_MAP = "Map<String, Object>"


            fun compile(comment: String): Struct {
                val structText = StructText.deconstruct(comment)
                // 先解析结构类型
                val structType = analyzeStructType(structText)
                // 解析出Java类型
                val javaType = analyzeJavaType(structText, structType)
                val fieldList = analyzeFieldList(structText.fieldList)
                return Struct(
                    structText.struct,
                    structType,
                    javaType,
                    structText.className,
                    fieldList,
                    fieldList.isNotEmpty(),
                    analyzeImportPackages(structText.struct)
                )
            }

            /**
             * 解析结构体类型
             * */
            private fun analyzeStructType(structText: StructText): StructType {
                return STRUCT_TYPE_MAP[structText.typeSymbol] ?: StructType.EMPTY
            }

            /**
             * 解析Java类型
             * */
            private fun analyzeJavaType(structText: StructText, structType: StructType): String {
                val (_, _, structBlock, javaType) = structText
                return when (structType) {
                    StructType.OBJECT -> {
                        if (structBlock.isNotBlank()) {
                            javaType
                        } else {
                            structType.defaultJavaType
                        }
                    }

                    StructType.LIST, StructType.REDUNDANCY, StructType.REDUNDANCY_LIST -> {
                        // 这三个类型要替换占位符
                        val typeReplace = when {
                            structBlock.isBlank() -> {
                                JAVA_TYPE_OBJECT
                            }

                            structType == StructType.LIST -> {
                                structBlock
                            }

                            else -> {
                                StringKit.camelCase(structBlock).capitalize()
                            }
                        }
                        structType.defaultJavaType.format(typeReplace)
                    }

                    StructType.OBJECT_LIST -> {
                        val typeReplace = if (structBlock.isNotBlank()) {
                            javaType
                        } else {
                            JAVA_TYPE_MAP
                        }
                        structType.defaultJavaType.format(typeReplace)
                    }

                    else -> {
                        // 默认全部返回Object
                        StructType.EMPTY.defaultJavaType
                    }
                }
            }

            private fun analyzeFieldList(fieldList: List<String>): List<JsonField> {
                return fieldList.map { JsonField.build(it) }.toList()
            }

            private fun analyzeImportPackages(struct: String): Set<String> {
                // 解析需要导入的包
                // 凡是有 '[' 或 ']'标识过的，说明都需要导入List
                // 凡是有 "{}" 标识过的，需要导入Map
                // 凡是有BigDecimal的，需要导入BigDecimal
                return IMPORT_CHECK_LIST.mapNotNull {
                    if (struct.indexOf(it) >= 0) {
                        IMPORT_MAP[it]
                    } else {
                        null
                    }
                }.toSet()
            }

        }

    }

    /**
     * 结构体解构后的文本对象
     * */
    private data class StructText(
        /**
         * 原始结构图语句
         * */
        val struct: String,
        /**
         * 类型标识
         * */
        val typeSymbol: String,
        /**
         * 结构体块
         * */
        val structBlock: String,
        /**
         * 解析出来的类名
         * */
        val className: String,
        /**
         * 解析出来的字段列表
         * */
        val fieldList: List<String> = emptyList()
    ) {
        companion object {
            fun deconstruct(comment: String): StructText {
                val struct = (STRUCT_TEXT_REGEX.find(comment)?.value ?: "").replace(STRUCT_PREFIX, "")
                // javaType部分
                val javaType = struct.replace(STRUCT_BLOCK_REGEX, "")
                // typeSymbol由左、右两边符号组成
                val leftSymbol = LEFT_TYPE_SYMBOL_REGEX.find(struct)?.value ?: ""
                val rightSymbol = RIGHT_TYPE_SYMBOL_REGEX.find(struct)?.value ?: ""

                // 取出结构体描述的部分
                val structBlock = (STRUCT_BLOCK_REGEX.find(struct)?.value ?: "")

                val fieldList = JSON_FIELD_REGEX.findAll(structBlock).map { it.value.replace(",", "") }.toList()

                return StructText(
                    struct,
                    "$leftSymbol$rightSymbol",
                    structBlock.substring(leftSymbol.length, structBlock.length - rightSymbol.length),
                    javaType,
                    fieldList
                )
            }
        }
    }

    enum class StructType(
        val leftSymbol: String,
        val rightSymbol: String,
        val defaultJavaType: String,
        private val description: String
    ) {
        EMPTY("", "", "Object", "空对象"),
        OBJECT("{", "}", "Map<String, Object>", "对象"),
        LIST("[", "]", "List<%s>", "列表"),
        OBJECT_LIST("[{", "}]", "List<%s>", "对象列表"),
        REDUNDANCY("RE{", "}", "%s", "冗余对象"),
        REDUNDANCY_LIST("RE[", "]", "List<%s>", "冗余列表")
    }

    data class JsonField(
        /**
         * 类型
         * */
        val type: String,
        /**
         * 字段名
         * */
        val name: String,
        /**
         * 注释
         * */
        val comment: String
    ) {

        companion object {
            fun build(fieldText: String): JsonField {
                val split = fieldText.split(":")
                val name = split[0]
                val typeAndComment = split[1]
                val type = typeAndComment.replace(JSON_FIELD_COMMENT_REGEX, "")
                    .replace("[", "List<")
                    .replace("]", ">")
                val comment = (JSON_FIELD_COMMENT_REGEX.find(typeAndComment)?.value ?: "")
                    .replace("(", "")
                    .replace(")", "")
                return JsonField(if (type == "{}") JAVA_TYPE_MAP else type, name, comment)
            }
        }

    }

}

fun main() {
    val struct =
        "STRUCT:ExternalAttr[{type:Integer(类型),name:String(名称),text:Text{value:String(内容)}(文本),web:{}(网页),miniprogram:{}(小程序)}]"
    // typeSymbol由左、右两边符号组成
    val leftSymbol = LEFT_TYPE_SYMBOL_REGEX.find(struct)?.value ?: ""
    val rightSymbol = RIGHT_TYPE_SYMBOL_REGEX.find(struct)?.value ?: ""

    // 取出结构体描述的部分
    val structBlock = STRUCT_BLOCK_REGEX.find(struct)?.value ?: ""

    val toList = JSON_FIELD_REGEX.findAll(structBlock).map { it.value }.toList()
    println(structBlock.substring(leftSymbol.length, structBlock.length - rightSymbol.length))
    println(toList)
    println(structBlock)
}